/**
 * @(#)PaletteLookAndFeel.java
 *
 * Copyright (c) 2008 by the original authors of JHotDraw and all its
 * contributors. All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the 
 * license agreement you entered into with the copyright holders. For details
 * see accompanying license terms.
 */
package org.jhotdraw.gui.plaf.palette;

import java.awt.Color;
import java.awt.Font;
import java.awt.Insets;
import java.util.Enumeration;
import java.util.ResourceBundle;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.UIDefaults;
import javax.swing.UIDefaults.ActiveValue;
import javax.swing.UIDefaults.LazyValue;
import javax.swing.UIDefaults.ProxyLazyValue;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.DimensionUIResource;
import javax.swing.plaf.InsetsUIResource;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicLookAndFeel;

/**
 * A LookAndFeel for components in the palette windows of a drawing editor.
 * 
 * @author Werner Randelshofer
 * @version $Id: PaletteLookAndFeel.java 717 2010-11-21 12:30:57Z rawcoder $
 */
public class PaletteLookAndFeel extends BasicLookAndFeel {

	/**
	 * Shared instance.
	 */
	private static PaletteLookAndFeel instance;
	/**
	 * Cached defaults.
	 */
	private UIDefaults cachedDefaults;

	public static PaletteLookAndFeel getInstance() {
		if (instance == null) {
			instance = new PaletteLookAndFeel();
		}
		return instance;
	}

	@Override
	public String getName() {
		return "Palette Look and Feel";
	}

	@Override
	public String getID() {
		return "Palette";
	}

	@Override
	public String getDescription() {
		return "A look and feel for palette components";
	}

	@Override
	public boolean isNativeLookAndFeel() {
		return false;
	}

	@Override
	public boolean isSupportedLookAndFeel() {
		return true;
	}

	public UIDefaults getCachedDefaults() {
		if (cachedDefaults == null) {
			cachedDefaults = getDefaults();
		}
		return cachedDefaults;
	}

	/**
	 * Looks up up the given key in our Hashtable and resolves LazyValues or
	 * ActiveValues.
	 */
	public Object get(Object key) {
		UIDefaults defaults = getCachedDefaults();

		/*
		 * Quickly handle the common case, without grabbing a lock.
		 */
		Object value = defaults.get(key);
		if (!(value instanceof ActiveValue) && !(value instanceof LazyValue)) {
			return value;
		}

		return value;
	}

	public boolean getBoolean(String key) {
		return ((Boolean) get(key)).booleanValue();
	}

	public Border getBorder(String key) {
		return (Border) get(key);
	}

	public Color getColor(String key) {
		return (Color) get(key);
	}

	public Font getFont(String key) {
		return (Font) get(key);
	}

	public Icon getIcon(String key) {
		return (Icon) get(key);
	}

	public int getInt(String key) {
		return (Integer) get(key);
	}

	public Insets getInsets(String key) {
		return (Insets) get(key);
	}

	public String getString(String key) {
		return (String) get(key);
	}

	/**
	 * Convenience method for initializing a components foreground background
	 * and font properties with values from the current defaults table. The
	 * properties are only set if the current value is either null or a
	 * UIResource.
	 * 
	 * @param c
	 *            the target component for installing default color/font
	 *            properties
	 * @param defaultBgName
	 *            the key for the default background
	 * @param defaultFgName
	 *            the key for the default foreground
	 * @param defaultFontName
	 *            the key for the default font
	 * 
	 * @see #installColors
	 * @see UIManager#getColor
	 * @see UIManager#getFont
	 */
	public static void installColorsAndFont(JComponent c, String defaultBgName, String defaultFgName, String defaultFontName) {
		Font f = c.getFont();
		if (f == null || f instanceof UIResource) {
			c.setFont(getInstance().getFont(defaultFontName));
		}

		installColors(c, defaultBgName, defaultFgName);
	}

	/**
	 * Convenience method for installing a component's default Border object on
	 * the specified component if either the border is currently null or already
	 * an instance of UIResource.
	 * 
	 * @param c
	 *            the target component for installing default border
	 * @param defaultBorderName
	 *            the key specifying the default border
	 */
	public static void installBorder(JComponent c, String defaultBorderName) {
		Border b = c.getBorder();
		if (b == null || b instanceof UIResource) {
			c.setBorder(getInstance().getBorder(defaultBorderName));
		}

	}

	/**
	 * Convenience method for initializing a component's foreground and
	 * background color properties with values from the current defaults table.
	 * The properties are only set if the current value is either null or a
	 * UIResource.
	 * 
	 * @param c
	 *            the target component for installing default color/font
	 *            properties
	 * @param defaultBgName
	 *            the key for the default background
	 * @param defaultFgName
	 *            the key for the default foreground
	 * 
	 * @see #installColorsAndFont
	 * @see UIManager#getColor
	 */
	public static void installColors(JComponent c, String defaultBgName, String defaultFgName) {
		PaletteLookAndFeel plaf = getInstance();
		Color bg = c.getBackground();
		if (bg == null || bg instanceof UIResource) {
			c.setBackground(plaf.getColor(defaultBgName));
		}

		Color fg = c.getForeground();
		if (fg == null || fg instanceof UIResource) {
			c.setForeground(plaf.getColor(defaultFgName));
		}
	}

	@Override
	protected void initComponentDefaults(UIDefaults table) {
		super.initComponentDefaults(table);

		// Add resource bundle to the table.
		// Since this does not seem to work in sandboxed environments, we check
		// whether we succeeded and - in case of failure - put the values in
		// by ourselves.
		table.addResourceBundle("org.jhotdraw.gui.Labels");
		if (table.getString("ColorChooser.rgbSliders") == null) {
			ResourceBundle rb = ResourceBundle.getBundle("org.jhotdraw.gui.Labels");
			for (Enumeration<String> keys = rb.getKeys(); keys.hasMoreElements();) {
				String key = keys.nextElement();
				table.put(key, rb.getObject(key));
			}
		}
		// *** Shared Fonts
		Integer fontPlain = Font.PLAIN;
		Integer fontBold = Font.BOLD;
		Object dialogPlain11 = new ProxyLazyValue("javax.swing.plaf.FontUIResource", null, new Object[] { "Dialog Sans", fontPlain, 11 });
		Object dialogPlain12 = new ProxyLazyValue("javax.swing.plaf.FontUIResource", null, new Object[] { "Dialog Sans", fontPlain, 12 });
		Object fieldPlain12 = new ProxyLazyValue("javax.swing.plaf.FontUIResource", null, new Object[] { "Verdana", fontPlain, 12 });
		Object dialogBold12 = new ProxyLazyValue("javax.swing.plaf.FontUIResource", null, new Object[] { "Dialog", fontBold, 12 });

		// *** Shared Colors
		ColorUIResource black = new ColorUIResource(Color.black);

		ColorUIResource control = new ColorUIResource(0xf0f0f0);
		ColorUIResource controlText = black;

		ColorUIResource selectionBackground = new ColorUIResource(0xb5d5ff);
		ColorUIResource selectionForeground = black;

		ColorUIResource listSelectionBackground = new ColorUIResource(0x3875d7);
		Object focusCellHighlightBorder = new UIDefaults.ProxyLazyValue("javax.swing.plaf.BorderUIResource$LineBorderUIResource",
		// null,
				new Object[] { listSelectionBackground });

		// *** Shared Insets
		InsetsUIResource zeroInsets = new InsetsUIResource(0, 0, 0, 0);

		// *** Shared Borders
		/*
		 * Object buttonBorder = new ProxyLazyValue(
		 * "org.jhotdraw.gui.plaf.palette.BackdropBorder$UIResource", new
		 * Object[] {new PaletteButtonBorder()});
		 */
		Object buttonBorder = new BackdropBorder.UIResource(new PaletteButtonBorder());
		Object textBorder = new BackdropBorder.UIResource(new PaletteTextComponentBorder());

		Object[] defaults = {
				// *** Fonts
				"SmallSystemFont",
				dialogPlain11,
				// *** Buttons
				"Button.font",
				dialogPlain12,
				"Button.background",
				control,
				"Button.foreground",
				controlText,
				"Button.border",
				buttonBorder,
				"Button.margin",
				zeroInsets,
				// *** FontChooser
				"Button.background",
				control,
				"Button.foreground",
				controlText,
				"Button.border",
				buttonBorder,
				"Button.margin",
				zeroInsets,
				// *** ColorChooser
				// class names of default choosers
				"ColorChooser.font",
				dialogPlain11,
				"ColorChooser.defaultChoosers",
				new String[] { "org.jhotdraw.gui.plaf.palette.colorchooser.PaletteSwatchesChooser",
						"org.jhotdraw.gui.plaf.palette.colorchooser.PaletteColorWheelChooser",
						"org.jhotdraw.gui.plaf.palette.colorchooser.PaletteColorSlidersChooser", },
				"ColorChooser.textSliderGap",
				3,
				//
				// *** FormattedTextField
				"FormattedTextField.font",
				fieldPlain12,
				"FormattedTextField.background",
				control,
				"FormattedTextField.foreground",
				controlText,
				"FormattedTextField.border",
				textBorder,
				"FormattedTextField.margin",
				zeroInsets,
				"FormattedTextField.opaque",
				Boolean.TRUE,
				"FormattedTextField.errorIndicatorForeground",
				new ColorUIResource(0xfe4a41),
				"FormattedTextField.selectionBackground",
				selectionBackground,
				"FormattedTextField.selectionForeground",
				selectionForeground,
				// *** Labels
				"Label.font",
				dialogPlain12,
				"Label.border",
				new UIDefaults.ProxyLazyValue("javax.swing.plaf.BorderUIResource$EmptyBorderUIResource", new Object[] { 0, 0, 0, 0 }),
				// *** Lists
				"List.focusCellHighlightBorder",
				focusCellHighlightBorder,
				"List.cellRenderer",
				new UIDefaults.ProxyLazyValue("org.jhotdraw.gui.plaf.palette.PaletteListCellRenderer"),
				// *** Panels
				"Panel.background",
				control,
				"Panel.foreground",
				controlText,
				"Panel.opaque",
				Boolean.TRUE,
				// *** Ribbons
				"Ribbon.border",
				new UIDefaults.ProxyLazyValue("javax.swing.border.MatteBorder",
						new Object[] { new Insets(1, 0, 0, 0), new Color(0x777777) }), //
				// *** ScrollPane
				"ScrollPane.border",
				new UIDefaults.ProxyLazyValue("javax.swing.border.MatteBorder",
						new Object[] { new Insets(1, 1, 1, 1), new Color(0xa5a5a5) }), //
				// *** Slider
				"Slider.background",
				control,
				"Slider.foreground",
				controlText,
				"Slider.horizontalSize",
				new DimensionUIResource(100, 20),
				"Slider.verticalSize",
				new DimensionUIResource(20, 100),
				"Slider.northThumb.small",
				new UIDefaults.ProxyLazyValue("org.jhotdraw.gui.plaf.palette.PaletteSliderThumbIcon", new Object[] {
						"/org/jhotdraw/gui/plaf/palette/images/Slider.northThumbs.small.png", 6, true }),
				"Slider.westThumb.small",
				new UIDefaults.ProxyLazyValue("org.jhotdraw.gui.plaf.palette.PaletteSliderThumbIcon", new Object[] {
						"/org/jhotdraw/gui/plaf/palette/images/Slider.westThumbs.small.png", 6, true }),
				// *** TabbedPane
				"TabbedPane.font", dialogPlain12, "TabbedPane.selectedFont", dialogBold12, "TabbedPane.background", control,
				"TabbedPane.contentAreaColor", control, "TabbedPane.foreground", controlText, "TabbedPane.highlight",
				new ColorUIResource(0xa5a5a5), "TabbedPane.lightHighlight", new ColorUIResource(0xa5a5a5), "TabbedPane.shadow",
				new ColorUIResource(0xa5a5a5),
				"TabbedPane.darkShadow",
				new ColorUIResource(0x333333),
				// *** TextArea
				"TextArea.selectionBackground", selectionBackground,
				"TextArea.selectionForeground",
				selectionForeground,
				// *** TextField
				"TextField.font", fieldPlain12, "TextField.background", control, "TextField.foreground", controlText, "TextField.border",
				textBorder, "TextField.margin", zeroInsets, "TextField.opaque", Boolean.TRUE, "TextField.selectionBackground",
				selectionBackground, "TextField.selectionForeground", selectionForeground,
				// *** ToolBar
				"ToolBar.font", dialogPlain12, "ToolBar.background", control, "ToolBar.foreground", controlText,
				"ToolBar.dockingBackground", control,
				// "ToolBar.dockingForeground", red,
				"ToolBar.floatingBackground", control,
				// "ToolBar.floatingForeground", darkGray,
				// "ToolBar.border", etchedBorder,
				"ToolBar.border", new UIDefaults.ProxyLazyValue("org.jhotdraw.gui.plaf.palette.PaletteToolBarBorder$UIResource"), //
		// "ToolBar.separatorSize", toolBarSeparatorSize,
		};
		table.putDefaults(defaults);
	}

	/**
	 * Returns the ui that is of type <code>klass</code>, or null if one can not
	 * be found.
	 */

	static Object getUIOfType(ComponentUI ui, Class klass) {
		if (klass.isInstance(ui)) {
			return ui;
		}
		return null;
	}
}
